// OscilloscopeCtrl.cpp : implementation file
//
#include "stdafx.h"
#include "resource.h"
#include "OscilloscopeCtrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// COscilloscopeBand

COscilloscopeBand::COscilloscopeBand()
{
}

COscilloscopeBand::COscilloscopeBand(LPCTSTR lpszName, COLORREF crColor, int nWidth, UINT uLower, UINT uUpper, UINT uOffset)
	:m_strName(lpszName), m_uDotOffset(uOffset)
{			
	SetRange(uLower, uUpper);
	m_pen.CreatePen(PS_SOLID, nWidth, crColor);
}

COscilloscopeBand::~COscilloscopeBand()
{
}

void COscilloscopeBand::SetRange(UINT uLower, UINT uUpper)
{
	ASSERT(uLower && uUpper && uLower < uUpper);
	m_uUpper = uUpper - uLower + 1;
	m_uLower = 1;
	m_uOffset = uLower - 1;
}

void COscilloscopeBand::SetPen(int nWidth, COLORREF crColor)
{
	m_pen.DeleteObject();
	m_pen.CreatePen(PS_SOLID, nWidth, crColor);
}

void COscilloscopeBand::SetPos(UINT uPos)
{
	uPos -= m_uOffset;
	ASSERT(uPos <= m_uUpper && uPos >= m_uLower);
	m_List.AddHead(uPos);
}

void COscilloscopeBand::SetOffset(UINT uOffset)
{
	m_uDotOffset = uOffset;
}

void COscilloscopeBand::DrawBand(CDC* pDC, RECT& rcBand)
{
	if (!pDC->GetSafeHdc()) {
		return;	
	}

	UINT uAverage = GetAverage();
	TRACE(_T("uAverage: %u\r\n"), uAverage);

	if (uAverage) {
		m_uPosY = rcBand.bottom - (uAverage * rcBand.bottom / m_uUpper);
	}		

	if (m_uPreviousPosY == (unsigned)rcBand.bottom + 1) {
		m_uPreviousPosY = m_uPosY;
	}		

	//CPen* pPen = pDC->SelectObject(&m_pen);
	pDC->SelectObject(&m_pen);
	pDC->MoveTo(rcBand.right - 11 - m_uDotOffset, m_uPreviousPosY);
	pDC->LineTo(rcBand.right - 11, m_uPosY);
	//pDC->SelectObject(pPen);

	m_uPreviousPosY = m_uPosY;	
}

UINT COscilloscopeBand::GetAverage()
{
	UINT uCounter = 0, uSum = 0;

	while (!m_List.IsEmpty()) {
		uSum += m_List.RemoveTail();
		uCounter++;
	}

	return uCounter ? uSum / uCounter : 0;
}

/////////////////////////////////////////////////////////////////////////////
// COscilloscopeCtrl

COscilloscopeCtrl::COscilloscopeCtrl()
{
	m_nFirstLinePos = 30;
	m_pMemDC = NULL;
	m_nSpeed = spdIdle;
	m_crBackGround = RGB(0, 0, 0);
	m_crGrid = RGB(0, 130, 66);

	m_uDotOffset = 4;
	m_uGridOffset = 30;//13;
}

COscilloscopeCtrl::~COscilloscopeCtrl()
{
	if (m_pMemDC) {
		delete m_pMemDC;
	}
	if (m_Bands.GetSize() == 0) {
		return;
	}

	for (int iIndex = m_Bands.GetSize() - 1; iIndex >= 0; iIndex--) {		
		delete m_Bands.GetAt(iIndex);
	}
}

BOOL COscilloscopeCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT uID)
{
	BOOL bRet = CWnd::CreateEx(
		WS_EX_STATICEDGE, 
		AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW),
		NULL,
		dwStyle,
		rect.left,
		rect.top,
		rect.right - rect.left,
		rect.bottom - rect.top,
		pParentWnd->GetSafeHwnd(),
		(HMENU)uID);

	if (!bRet) {
		return FALSE;
	}		

	m_pMemDC = new CDC;
	if (!m_pMemDC) {
		return FALSE;
	}		

	GetClientRect(m_rcClient);

	if (!InvalidateCtrl()) {
		return FALSE;	
	}

	return TRUE;
}

void COscilloscopeCtrl::SetOffset(UINT uDot, UINT uGrid)
{
	if (1 > uDot) {
		uDot = 1;
	} else if (10 < uDot) {
		uDot = 10;
	}
	m_uDotOffset = uDot;
	for (int i=0; i<m_Bands.GetCount(); i++) {
		m_Bands.GetAt(i)->SetOffset(m_uDotOffset);
	}

	if (10 > uGrid) {
		uGrid = 10;
	}else if (50 < uGrid) {
		uGrid = 50;
	}
	m_uGridOffset = uGrid;
}

BOOL COscilloscopeCtrl::SetGridColor(COLORREF crGrid)
{
	BOOL bRet = FALSE;	

	m_crGrid = crGrid;

	CClientDC dc(this);

	int nOldPos = m_nFirstLinePos;
	m_nFirstLinePos = m_uGridOffset;

	if (m_pMemDC->DeleteDC()) {
		if (InvalidateCtrl()) {
			bRet = TRUE;
		}			
	}		

	if (!bRet) {
		m_nFirstLinePos = nOldPos;
	}	

	return bRet;
}

BOOL COscilloscopeCtrl::SetBkColor(COLORREF crBk)
{
	BOOL bRet = FALSE;	

	m_crBackGround = crBk;

	CClientDC dc(this);

	int nOldPos = m_nFirstLinePos;

	m_nFirstLinePos = m_uGridOffset;

	if (m_pMemDC->DeleteDC()) {
		if (InvalidateCtrl()) {
			bRet = TRUE;
		}			
	}		

	if (!bRet) {
		m_nFirstLinePos = nOldPos;
	}	

	return bRet;
}

void COscilloscopeCtrl::DrawBands()
{	
	if (!m_pMemDC->GetSafeHdc()) {
		return;
	}

	CRect bkRect(m_rcClient.right - m_uDotOffset, m_rcClient.top, m_rcClient.right, m_rcClient.bottom);

	CBrush bkBrush;
	bkBrush.CreateSolidBrush(m_crBackGround);
	m_pMemDC->FillRect(bkRect, &bkBrush);

	m_pMemDC->BitBlt(0, 0, m_rcClient.Width(), m_rcClient.Height(), m_pMemDC, m_uDotOffset, 0, SRCCOPY);

	CPen pen;
	pen.CreatePen(PS_SOLID, 1, m_crGrid);
	CPen* pPen = m_pMemDC->SelectObject(&pen);

	m_nFirstLinePos -= m_uDotOffset;
	if (m_nFirstLinePos < 0) {
		m_nFirstLinePos += m_uGridOffset;
	}

	int nX = m_rcClient.right - ((m_rcClient.right - m_nFirstLinePos) % m_uGridOffset) - 1;
	m_pMemDC->MoveTo(nX, m_rcClient.top);
	m_pMemDC->LineTo(nX, m_rcClient.bottom);

	for (register int j = m_rcClient.top - 1; j < m_rcClient.bottom; j += m_uGridOffset) {
		m_pMemDC->MoveTo(bkRect.left - m_uDotOffset, j);
		m_pMemDC->LineTo(bkRect.right, j);
	}	

	m_pMemDC->SelectObject(pPen);

	int nCount = m_Bands.GetSize();
	for (int nIndex = 0; nIndex < nCount; ++nIndex) {
		m_Bands[nIndex]->DrawBand(m_pMemDC, m_rcClient);
	}

	Invalidate();
}

BOOL COscilloscopeCtrl::InvalidateCtrl()
{
	CClientDC dc(this);

	if (m_pMemDC->GetSafeHdc()) {
		return FALSE;
	}

	if (!m_pMemDC->CreateCompatibleDC(&dc)) {
		return FALSE;
	}

	CBitmap bmp;
	if (!bmp.CreateCompatibleBitmap(&dc, m_rcClient.Width(), m_rcClient.Height())) {
		return FALSE;
	}

	if (!m_pMemDC->SelectObject(bmp)) {
		return FALSE;
	}

	//Set the background color of the control
	CBrush bkBrush;
	if (!bkBrush.CreateSolidBrush(m_crBackGround)) {
		return FALSE;
	}

	m_pMemDC->FillRect(m_rcClient, &bkBrush);

	//Select a specified pen to the device context to draw background lines
	CPen bkLinesPen;
	if (!bkLinesPen.CreatePen(PS_SOLID, 1, m_crGrid)) {
		return FALSE;
	}

	if (!m_pMemDC->SelectObject(bkLinesPen)) {
		return FALSE;
	}

	//Draw background lines
	for (register int i = m_rcClient.left - 1; i < m_rcClient.right; i += m_uGridOffset) {
		m_pMemDC->MoveTo(i, m_rcClient.top);
		m_pMemDC->LineTo(i, m_rcClient.bottom);
	}

	for (register int j = m_rcClient.top - 1; j < m_rcClient.bottom; j += m_uGridOffset) {
		m_pMemDC->MoveTo(m_rcClient.left, j);
		m_pMemDC->LineTo(m_rcClient.right, j);
	}		

	int nCount = m_Bands.GetSize();
	for (int nIndex = 0; nIndex < nCount; ++nIndex) {
		m_Bands[nIndex]->m_uPreviousPosY = m_Bands[nIndex]->m_uPosY = m_rcClient.bottom + 1;
	}

	InvalidateRect(m_rcClient);

	return TRUE;
}

BEGIN_MESSAGE_MAP(COscilloscopeCtrl, CWnd)
	//{{AFX_MSG_MAP(COscilloscopeCtrl)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COscilloscopeCtrl message handlers

void COscilloscopeCtrl::OnPaint() 
{
	CPaintDC dc(this);

	if (m_pMemDC->GetSafeHdc()) {
		dc.BitBlt(0, 0, m_rcClient.Width(), m_rcClient.Height(), m_pMemDC, 0, 0, SRCCOPY);
	}
}
